/*-------------------------------------------------------------------------
 *
 * amapi.c
 *	  Postgres 索引访问方法的 API 的支持例程。
 *
 * Copyright (c) 2015-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/index/amapi.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/amapi.h"
#include "access/htup_details.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
#include "utils/builtins.h"
#include "utils/syscache.h"


/*
 * GetIndexAmRoutine - 调用指定的访问方法处理例程以获取
 * 其 IndexAmRoutine 结构，该结构将在调用者的上下文中 palloc。
 *
 * 请注意，如果 amhandler 函数是内置的，则这不会涉及
 * 任何目录访问。因此，在引导系统目录的索引时使用它是安全的。
 * relcache.c 依赖于此。
 */
IndexAmRoutine *
GetIndexAmRoutine(Oid fc_amhandler)
{
	Datum		fc_datum;
	IndexAmRoutine *fc_routine;

	fc_datum = OidFunctionCall0(fc_amhandler);
	fc_routine = (IndexAmRoutine *) DatumGetPointer(fc_datum);

	if (fc_routine == NULL || !IsA(fc_routine, IndexAmRoutine))
		elog(ERROR, "index access method handler function %u did not return an IndexAmRoutine struct",
			 fc_amhandler);

	return fc_routine;
}

/*
 * GetIndexAmRoutineByAmId - 查找具有给定 OID 的索引访问方法的处理程序，
 * 并获取其 IndexAmRoutine 结构。
 *
 * 如果给定的 OID 不是有效的索引访问方法，则如果 noerror 为真则返回 NULL，
 * 否则抛出错误。
 */
IndexAmRoutine *
GetIndexAmRoutineByAmId(Oid fc_amoid, bool fc_noerror)
{
	HeapTuple	fc_tuple;
	Form_pg_am	fc_amform;
	regproc		fc_amhandler;

	/* 获取访问方法的处理程序函数 OID */
	fc_tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(fc_amoid));
	if (!HeapTupleIsValid(fc_tuple))
	{
		if (fc_noerror)
			return NULL;
		elog(ERROR, "cache lookup failed for access method %u",
			 fc_amoid);
	}
	fc_amform = (Form_pg_am) GETSTRUCT(fc_tuple);

	/* 检查它是否是索引访问方法，而不是其他 AM */
	if (fc_amform->amtype != AMTYPE_INDEX)
	{
		if (fc_noerror)
		{
			ReleaseSysCache(fc_tuple);
			return NULL;
		}
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("access method \"%s\" is not of type %s",
						NameStr(fc_amform->amname), "INDEX")));
	}

	fc_amhandler = fc_amform->amhandler;

	/* 如果处理程序 OID 无效，则发出警告 */
	if (!RegProcedureIsValid(fc_amhandler))
	{
		if (fc_noerror)
		{
			ReleaseSysCache(fc_tuple);
			return NULL;
		}
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("index access method \"%s\" does not have a handler",
						NameStr(fc_amform->amname))));
	}

	ReleaseSysCache(fc_tuple);

	/* 最后，调用处理程序函数以获取 API 结构。 */
	return GetIndexAmRoutine(fc_amhandler);
}


/*
 * 请求适当的访问方法验证指定的 opclass。
 */
Datum amvalidate(PG_FUNCTION_ARGS)
{
	Oid			fc_opclassoid = PG_GETARG_OID(0);
	bool		fc_result;
	HeapTuple	fc_classtup;
	Form_pg_opclass fc_classform;
	Oid			fc_amoid;
	IndexAmRoutine *fc_amroutine;

	fc_classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(fc_opclassoid));
	if (!HeapTupleIsValid(fc_classtup))
		elog(ERROR, "cache lookup failed for operator class %u", fc_opclassoid);
	fc_classform = (Form_pg_opclass) GETSTRUCT(fc_classtup);

	fc_amoid = fc_classform->opcmethod;

	ReleaseSysCache(fc_classtup);

	fc_amroutine = GetIndexAmRoutineByAmId(fc_amoid, false);

	if (fc_amroutine->amvalidate == NULL)
		elog(ERROR, "function amvalidate is not defined for index access method %u",
			 fc_amoid);

	fc_result = fc_amroutine->amvalidate(fc_opclassoid);

	pfree(fc_amroutine);

	PG_RETURN_BOOL(fc_result);
}
